#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
智能地理编码服务 - 结合BIM数据
1. 使用高德地图获取小区基础位置
2. 结合BIM数据中的建筑坐标进行精确定位
"""

import pandas as pd
import requests
import json
import time
import os
import re
from datetime import datetime
import logging
import math

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('geocoding_with_bim.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


class BIMDataExtractor:
    """从common.js提取BIM数据"""
    
    @staticmethod
    def extract_bim_coordinates():
        """提取BIM建筑坐标数据"""
        bim_data = {}
        
        try:
            with open('common.js', 'r', encoding='utf-8') as f:
                content = f.read()
            
            # 提取queryBimData函数中的数据
            # 查找包含坐标的数据
            pattern = r"lng:([\d.]+),\s*lat:([\d.]+),.*?d:'([^']+)',.*?title:'([^']+)'"
            matches = re.findall(pattern, content)
            
            for match in matches:
                lng, lat, d_code, title = match
                bim_data[d_code] = {
                    'longitude': float(lng),
                    'latitude': float(lat),
                    'title': title
                }
            
            # 提取queryBuildingData函数中的详细房间数据
            pattern2 = r'"lat":([\d.]+),"lng":([\d.]+),.*?"id":"([^"]+)"'
            matches2 = re.findall(pattern2, content)
            
            for match in matches2:
                lat, lng, room_id = match
                bim_data[room_id] = {
                    'longitude': float(lng),
                    'latitude': float(lat),
                    'room_id': room_id
                }
            
            logger.info(f"成功提取 {len(bim_data)} 条BIM坐标数据")
            return bim_data
            
        except Exception as e:
            logger.error(f"提取BIM数据失败: {str(e)}")
            return {}


class SmartGeocoder:
    """智能地理编码器 - 结合高德地图和BIM数据"""
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://restapi.amap.com/v3/geocode/geo"
        self.session = requests.Session()
        self.cache = {}
        self.bim_data = BIMDataExtractor.extract_bim_coordinates()
        
    def geocode_community(self, community_name: str):
        """获取小区的基础坐标"""
        if community_name in self.cache:
            return self.cache[community_name]
        
        params = {
            'key': self.api_key,
            'address': community_name,
            'output': 'json'
        }
        
        try:
            response = self.session.get(self.base_url, params=params, timeout=10)
            response.raise_for_status()
            data = response.json()
            
            if data['status'] == '1' and data['count'] != '0':
                geocode = data['geocodes'][0]
                location = geocode['location'].split(',')
                
                result = {
                    'success': True,
                    'longitude': float(location[0]),
                    'latitude': float(location[1]),
                    'formatted_address': geocode.get('formatted_address', ''),
                    'province': geocode.get('province', ''),
                    'city': geocode.get('city', ''),
                    'district': geocode.get('district', '')
                }
                
                self.cache[community_name] = result
                return result
            else:
                return {'success': False}
                
        except Exception as e:
            logger.error(f"地理编码失败: {str(e)}")
            return {'success': False}
    
    def get_building_coordinates(self, building_number: str, room_number: str = None):
        """根据建筑编号获取精确坐标"""
        
        # 1. 首先尝试精确匹配房间ID
        if room_number:
            # 构建可能的房间ID格式
            possible_ids = [
                f"兴海嘉苑-{building_number}-{room_number}",
                f"安滨嘉苑-{building_number}-{room_number}",
                f"{building_number}-{room_number}"
            ]
            
            for room_id in possible_ids:
                if room_id in self.bim_data:
                    return self.bim_data[room_id]
        
        # 2. 尝试匹配建筑编号
        if building_number in self.bim_data:
            return self.bim_data[building_number]
        
        # 3. 尝试模糊匹配
        for key, data in self.bim_data.items():
            if building_number in key:
                return data
        
        return None
    
    def calculate_offset(self, base_lat: float, base_lng: float, 
                        building_number: str, index: int):
        """计算建筑物相对于小区中心的偏移量"""
        # 根据建筑编号计算偏移
        # 这是一个简单的算法，实际应用中可能需要更复杂的布局算法
        
        # 提取建筑编号中的数字
        match = re.search(r'(\d+)', building_number)
        if match:
            building_num = int(match.group(1))
        else:
            building_num = index
        
        # 计算行列位置（假设建筑按网格排列）
        row = (building_num - 1) // 10
        col = (building_num - 1) % 10
        
        # 每栋楼之间的间距（经纬度）
        spacing_lat = 0.0002  # 约22米
        spacing_lng = 0.0003  # 约30米
        
        # 计算偏移后的坐标
        offset_lat = base_lat + (row - 5) * spacing_lat
        offset_lng = base_lng + (col - 5) * spacing_lng
        
        return offset_lat, offset_lng


def process_house_data_with_bim(api_key: str):
    """处理房产数据 - 结合BIM坐标"""
    
    logger.info("开始处理房产权属数据（结合BIM数据）...")
    
    # 读取Excel文件
    try:
        df = pd.read_excel('house_owner.xls', sheet_name=0)
        logger.info(f"成功读取 {len(df)} 条记录")
        logger.info(f"列名: {list(df.columns)}")
        
    except Exception as e:
        logger.error(f"读取Excel文件失败: {str(e)}")
        return
    
    # 创建智能地理编码器
    geocoder = SmartGeocoder(api_key)
    
    # 初始化结果列
    df['longitude'] = None
    df['latitude'] = None
    df['geocode_method'] = ''  # 记录获取坐标的方法
    df['geocode_confidence'] = 0.0
    df['formatted_address'] = ''
    df['province'] = ''
    df['city'] = ''
    df['district'] = ''
    
    # 统计
    stats = {
        'bim_exact': 0,  # BIM精确匹配
        'bim_building': 0,  # BIM建筑匹配
        'calculated': 0,  # 计算得出
        'community_center': 0,  # 小区中心点
        'failed': 0  # 失败
    }
    
    # 获取小区基础坐标
    community_result = None
    if 'xqname' in df.columns:
        xqname = df['xqname'].iloc[0] if pd.notna(df['xqname'].iloc[0]) else None
        if xqname:
            logger.info(f"获取小区 '{xqname}' 的基础坐标...")
            community_result = geocoder.geocode_community(str(xqname))
            if community_result['success']:
                logger.info(f"小区中心坐标: ({community_result['longitude']}, {community_result['latitude']})")
    
    # 处理每条记录
    for index, row in df.iterrows():
        logger.info(f"\n处理 [{index+1}/{len(df)}]:")
        
        building_number = str(row.get('building_number', '')).strip() if 'building_number' in df.columns else ''
        room_number = str(row.get('room_number', '')).strip() if 'room_number' in df.columns else ''
        
        # 1. 尝试从BIM数据获取精确坐标
        bim_coords = geocoder.get_building_coordinates(building_number, room_number)
        
        if bim_coords:
            df.at[index, 'longitude'] = bim_coords['longitude']
            df.at[index, 'latitude'] = bim_coords['latitude']
            df.at[index, 'geocode_method'] = 'BIM精确匹配' if room_number else 'BIM建筑匹配'
            df.at[index, 'geocode_confidence'] = 1.0 if room_number else 0.9
            
            if room_number:
                stats['bim_exact'] += 1
            else:
                stats['bim_building'] += 1
            
            logger.info(f"  {building_number} {room_number} -> BIM坐标: ({bim_coords['longitude']}, {bim_coords['latitude']})")
            
        # 2. 如果没有BIM数据，使用小区中心点+偏移计算
        elif community_result and community_result['success'] and building_number:
            offset_lat, offset_lng = geocoder.calculate_offset(
                community_result['latitude'],
                community_result['longitude'],
                building_number,
                index
            )
            
            df.at[index, 'longitude'] = offset_lng
            df.at[index, 'latitude'] = offset_lat
            df.at[index, 'geocode_method'] = '基于小区中心计算'
            df.at[index, 'geocode_confidence'] = 0.7
            stats['calculated'] += 1
            
            logger.info(f"  {building_number} -> 计算坐标: ({offset_lng}, {offset_lat})")
            
        # 3. 使用小区中心点
        elif community_result and community_result['success']:
            df.at[index, 'longitude'] = community_result['longitude']
            df.at[index, 'latitude'] = community_result['latitude']
            df.at[index, 'geocode_method'] = '小区中心点'
            df.at[index, 'geocode_confidence'] = 0.5
            stats['community_center'] += 1
            
            logger.info(f"  使用小区中心点: ({community_result['longitude']}, {community_result['latitude']})")
            
        else:
            df.at[index, 'geocode_method'] = '失败'
            df.at[index, 'geocode_confidence'] = 0.0
            stats['failed'] += 1
            logger.warning(f"  无法获取坐标")
        
        # 填充地址信息
        if community_result and community_result['success']:
            df.at[index, 'formatted_address'] = community_result.get('formatted_address', '')
            df.at[index, 'province'] = community_result.get('province', '')
            df.at[index, 'city'] = community_result.get('city', '')
            df.at[index, 'district'] = community_result.get('district', '')
    
    # 保存结果
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    output_file = f'house_owner_geocoded_bim_{timestamp}.xlsx'
    
    df.to_excel(output_file, index=False, engine='openpyxl')
    logger.info(f"\n结果已保存到: {output_file}")
    
    # 生成详细报告
    report = f"""
地理编码完成报告（结合BIM数据）
================================
处理时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
总记录数: {len(df)}

坐标获取方式统计:
  BIM精确匹配: {stats['bim_exact']} 条
  BIM建筑匹配: {stats['bim_building']} 条
  基于小区计算: {stats['calculated']} 条
  小区中心点: {stats['community_center']} 条
  失败: {stats['failed']} 条

成功率: {(len(df) - stats['failed']) / len(df) * 100:.2f}%
"""
    
    # 添加坐标范围统计
    valid_df = df[df['longitude'].notna()]
    if len(valid_df) > 0:
        lon_range = (valid_df['longitude'].min(), valid_df['longitude'].max())
        lat_range = (valid_df['latitude'].min(), valid_df['latitude'].max())
        
        report += f"""
坐标范围:
  经度: {lon_range[0]:.6f} ~ {lon_range[1]:.6f}
  纬度: {lat_range[0]:.6f} ~ {lat_range[1]:.6f}
  
坐标分布:
  不同经度值: {valid_df['longitude'].nunique()} 个
  不同纬度值: {valid_df['latitude'].nunique()} 个
"""
    
    # 保存报告
    report_file = f'house_owner_geocoded_bim_{timestamp}_report.txt'
    with open(report_file, 'w', encoding='utf-8') as f:
        f.write(report)
    
    logger.info(f"报告已保存到: {report_file}")
    logger.info("\n" + report)
    
    # 显示部分结果示例
    logger.info("\n结果示例（前10条不同坐标）:")
    sample_df = valid_df.drop_duplicates(subset=['longitude', 'latitude']).head(10)
    for _, row in sample_df.iterrows():
        logger.info(f"  {row.get('building_number', '')} {row.get('room_number', '')} -> "
                   f"({row['longitude']:.6f}, {row['latitude']:.6f}) - {row['geocode_method']}")


def main():
    """主函数"""
    API_KEY = "64fdb23b96e7a2c0c94c95a789ecef50"
    
    logger.info("=" * 60)
    logger.info("智能地理编码服务 - 结合BIM数据")
    logger.info("=" * 60)
    
    # 检查必要文件
    if not os.path.exists('house_owner.xls'):
        logger.error("找不到 house_owner.xls 文件！")
        return
    
    if not os.path.exists('common.js'):
        logger.error("找不到 common.js 文件！")
        return
    
    # 处理数据
    process_house_data_with_bim(API_KEY)


if __name__ == "__main__":
    main() 